﻿namespace EyeOpen.MappingGenerator.Windows
{
    using System;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Windows.Forms;
    using EyeOpen.Mapping;
    using EyeOpen.Mapping.SpecializedNhibernate;
    using EyeOpen.Mapping.Validation;
    using EyeOpen.Mapping.WcfContract;
    using System.Xml.Linq;

    internal partial class MainForm : Form
    {
        private CompilerErrorCollection compilerErrors = null;

        public MainForm()
        {
            this.InitializeComponent();
        }

        protected override void OnLoad(EventArgs e)
        {
            this.outputFolderTextBox.Text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "Mappings");
            this.typesComboBox.SelectedIndex = 2;
            this.InitializeLanguagesComboBox();
        }

        private void InitializeLanguagesComboBox()
        {
            this.languagesComboBox.Items.Clear();
            this.languagesComboBox.Items.Add(new Language { Name = "VB.NET", Extension = "vb" });
            this.languagesComboBox.Items.Add(new Language { Name = "C#", Extension = "cs" });
            this.languagesComboBox.DisplayMember = "Name";
            this.languagesComboBox.ValueMember = "Extension";
            this.languagesComboBox.SelectedIndex = 1;
        }

        private void PrepareOutputFolders(string outputDirectory, string nhibernateFolder, string validationFolder, string wcfContractsFolder)
        {
            if (Directory.Exists(outputDirectory))
            {
                DirectoryInfo[] subDirectory = new DirectoryInfo(outputDirectory).GetDirectories();

                foreach (DirectoryInfo directory in subDirectory)
                {
                    directory.Delete(true);
                }
            }
            else
            {
                ////Create the output directory
                Directory.CreateDirectory(outputDirectory);
            }

            Directory.CreateDirectory(nhibernateFolder);

            Directory.CreateDirectory(validationFolder);

            Directory.CreateDirectory(wcfContractsFolder);
        }

        private void GenerateMappings(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (!this.AreValidPaths())
            {
                return;
            }

            string assemblyPath = this.assemblyPathTextBox.Text;
            string outputDirectory = this.outputFolderTextBox.Text;

            DialogResult result;

            if (!Directory.Exists(outputDirectory))
            {
                result = MessageBox.Show("The specified output folder does not exists. Create?", "Output folder not exists", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);

                if (result != DialogResult.OK)
                {
                    MessageBox.Show("Operation aborted.", "Operation aborted", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }
            }

            result = MessageBox.Show("Starting to delete all sub directory. Proceed?", "Delete all sub directory", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);

            if (result != DialogResult.OK)
            {
                MessageBox.Show("Operation aborted.", "Operation aborted", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            #region "Folders initialization"
            string nhibernateFolder = Path.Combine(outputDirectory, "NHibernate");
            string validationFolder = Path.Combine(outputDirectory, "Validation");
            string wcfContractsFolder = Path.Combine(outputDirectory, "WCF Contracts");
            this.PrepareOutputFolders(outputDirectory, nhibernateFolder, validationFolder, wcfContractsFolder);
            result = MessageBox.Show("All directory are deleted.", "Directory deleted", MessageBoxButtons.OK, MessageBoxIcon.Information);
            #endregion

            ////Start the mapping
            Assembly assembly = Assembly.LoadFrom(assemblyPath);
            this.GenerateMappings(assembly, nhibernateFolder, validationFolder, wcfContractsFolder);

            MessageBox.Show("All templates are correctly generated.", "Generation done", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void GenerateMappings(Assembly assembly, string nhibernateFolder, string validationFolder, string wcfContractsFolder)
        {
            if (this.nhibernateCheckBox.Checked)
            {
                this.GenerateSpecializedNhibernateMapping(assembly, nhibernateFolder, this.generatorTypeNameTextBox.Text, this.chkDefaultNotNull.Checked);
            }

            if (this.validationCheckbox.Checked)
            {
                this.GenerateValidationMappings(assembly, validationFolder);
            }

            if (this.wcfContracts.Checked)
            {
                this.GenerateWcfContracts(assembly, wcfContractsFolder);
            }
        }

        private void GenerateWcfContracts(Assembly assembly, string wcfContractsFolder)
        {
            this.compilerErrorsListBox.Items.Clear();

            Language language = (Language) this.languagesComboBox.SelectedItem;
            WcfContractGenerator contractGenerator = new WcfContractGenerator(language.Extension, this.generateContractsAssemblyCheckBox.Checked);
            contractGenerator.CompletlyReplaceNamespace = this.namespaceReplaceCheckBox.Checked;
            contractGenerator.OriginalNamespace = this.originalNamespaceTextBox.Text;
            contractGenerator.ReplaceNamespace = this.replaceNamespaceTextBox.Text;
            contractGenerator.OutputFolder = wcfContractsFolder;

            contractGenerator.GenerateMappings(assembly)
                .ToList()
                .ForEach(map => 
                    File.WriteAllText(
                        Path.Combine(wcfContractsFolder, string.Format("{0}.{1}", map.Key.Name, language.Extension)),
                        contractGenerator.GetSourceCodeFromCodeNameSpace(map.Value)));

            this.compilerErrors = contractGenerator.CompilerErrors;

            if (contractGenerator.CompilerErrors != null)
            {                
                string[] compilerErrors = contractGenerator.CompilerErrors
                    .Cast<CompilerError>()
                    .Select<CompilerError, string>((compileError, b) => compileError.ErrorText)
                    .ToArray();
                foreach (string compilationError in compilerErrors) 
                {
                    this.compilerErrorsListBox.Items.Add(compilationError);
                }   
            }
        }

        private void GenerateValidationMappings(Assembly assembly, string validationFolder)
        {
            IDictionary<Type, IList<Validator>> validators = new Dictionary<Type, IList<Validator>>();

            IDictionary<string, object> rangeValidatorParameters = new Dictionary<string, object>();
            rangeValidatorParameters.Add("Min", 5);
            rangeValidatorParameters.Add("Max", 15);
            Validator rangeValidator = new Validator("RangeValidator", rangeValidatorParameters);
            validators.Add(typeof(string), new List<Validator> { new Validator("RequiredValidator") });
            validators.Add(typeof(int), new List<Validator> { rangeValidator });

            BaseMapper validationGenerator = new ValidationMapper(validators);
            validationGenerator.GenerateMappings(assembly)
                .ToList()
                .ForEach(map => map.Value.Save(Path.Combine(validationFolder, string.Format("{0}.validation.xml", map.Key.Name))));
        }

        private void GenerateSpecializedNhibernateMapping(Assembly assembly, string nhibernateFolder, string generatorTypeName, bool notNull)
        {
            BaseMapper nhibernateMapper = new SpecializedNhibernateMapper()
            {
                GeneratorTypeName = generatorTypeName,
                NotNullDefault = notNull,
                IdType = Type.GetType(this.typesComboBox.SelectedItem.ToString())
            };
            nhibernateMapper
                .GenerateMappings(assembly)
                .ToList()
                .ForEach(map => map.Value.Save(Path.Combine(nhibernateFolder, string.Format("{0}.hbm.xml", map.Key.Name))));
        }

        private bool AreValidPaths()
        {
            if (!File.Exists(this.assemblyPathTextBox.Text))
            {
                MessageBox.Show("Must specify a valid .NET assembly path.", "Assembly path not valid", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return false;
            }

            try
            {
                new DirectoryInfo(this.outputFolderTextBox.Text);
                return true;
            }
            catch
            {
                MessageBox.Show("Must specify a valid output folder.", "Output folder not valid", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return false;
            }
        }

        private void SelectAssemblyPath(object sender, LinkLabelLinkClickedEventArgs e)
        {
            OpenFileDialog ofdAssembly = new OpenFileDialog();
            ofdAssembly.DefaultExt = "dll";
            ofdAssembly.Filter = "Assembly files|*.exe;*.dll";
            ofdAssembly.Multiselect = false;
            DialogResult result = ofdAssembly.ShowDialog(this);
            if (result == DialogResult.OK) 
            {
                this.assemblyPathTextBox.Text = ofdAssembly.FileName;
            }
        }

        private void SelectOutputFolder(object sender, LinkLabelLinkClickedEventArgs e)
        {
            FolderBrowserDialog fbdOutputFolder = new FolderBrowserDialog();
            
            fbdOutputFolder.SelectedPath = this.outputFolderTextBox.Text;
            fbdOutputFolder.ShowNewFolderButton = true;            
            DialogResult result = fbdOutputFolder.ShowDialog(this);
            if (result == DialogResult.OK) 
            {
                this.outputFolderTextBox.Text = fbdOutputFolder.SelectedPath;
            }
        }

        private void NamespaceReplaceCheckBoxCheckedChanged(object sender, EventArgs e)
        {
            this.replaceNamespaceTextBox.Enabled = !this.namespaceReplaceCheckBox.Checked;
        }

        private void CompilerErrorsLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            CompilerErrorsForm compilerErrorsForm = new CompilerErrorsForm(this.compilerErrors);
            compilerErrorsForm.ShowDialog(this);
        }
    }
}
